home *** CD-ROM | disk | FTP | other *** search
- // Copyright (C) 1996, 1997 Meta Four Software. All rights reserved.
- //
- // Recursive directory scanner sample code
- //
- //! rev="$Id: scandisk.cpp,v 1.4 1997/05/27 00:06:14 jcw Rel $"
-
- /////////////////////////////////////////////////////////////////////////////
- //
- // The following two globally accesible routines are defined below:
- //
- // c4_View fScanDirectories(const char* path_);
- // CString fFullPath(c4_View& dirs_, int dirNum_);
- //
- // The fScanDirectories routine does all the work, and completely hides all
- // Windows 16/32 specifics by returning a generalized catalog tree object.
- // In contrast with normal C++ conventions, nearly all MetaKit objects are
- // reference counted. This takes care of fully automatic cleanup after use.
- //
- // The structure of the object returned by fScanDirectories is as follows:
- //
- // Property Type Description
- // ======== ==== ===========
- //
- // dirs nested Contains 1 record per directory
- // parent integer Index of parent entry
- // name string Name of this directory
- // files nested Contains 1 record per file entry
- // name string Name of this file
- // size string File size
- // date integer Modification date (DOS format 7+4+5 bits)
- //
- // The first directory entry (entry 0) is special: the parent is set to zero
- // (points to itself), and the name is the base path name (see path_ arg).
- //
- // In C++ notation, a declaration for this object would be something like:
- //
- // struct
- // {
- // int parent;
- // char* name;
- //
- // struct
- // {
- // char* name;
- // long size;
- // int date;
- //
- // } files []; // variable size, not allowed in C++
- //
- // } dirs [];
- //
- /////////////////////////////////////////////////////////////////////////////
-
- #include "stdafx.h"
- #include "scandisk.h"
-
- #ifndef _WIN32
- #define LFNAMES // define this to support long filenames in a 16-bit app
- #endif
-
- /////////////////////////////////////////////////////////////////////////////
- // Property definitions
-
- c4_ViewProp pFiles ("files");
- c4_IntProp pParent ("parent"),
- pSize ("size"),
- pDate ("date");
- c4_StringProp pName ("name");
-
- /////////////////////////////////////////////////////////////////////////////
-
- bool cStatusHandler::UpdateStatus(const char* /* text */)
- {
- MSG msg;
- while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- {
- if (msg.message == WM_QUIT)
- {
- ::PostQuitMessage(msg.wParam);
- return FALSE;
- }
- if (!AfxGetApp()->PreTranslateMessage(&msg))
- {
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg);
- }
- }
- AfxGetApp()->OnIdle(0); // updates user interface
- AfxGetApp()->OnIdle(1); // frees temporary objects
-
- return TRUE;
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // Reconstruct the full path name from a subdirectory index in the tree
-
- CString fFullPath(c4_View& dirs_, int dirNum_)
- {
- // Prefix all parent dir names until the root level is reached
- CString path;
- for (;;)
- {
- path = pName (dirs_[dirNum_]) + "\\" + path;
-
- if (dirNum_ == 0)
- break;
-
- dirNum_ = (int) pParent (dirs_[dirNum_]);
- }
-
- return path; // this result always has a trailing backslash
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // There are two versions of ScanSubDir, one for Win32 and one for Win16
-
- #ifdef _WIN32
-
- // Convert a Win32 filedate back to the good old DOS format
- static WORD DosDate(FILETIME& ft)
- {
- SYSTEMTIME st;
-
- if (!FileTimeToSystemTime(&ft, &st))
- return 0;
-
- return (WORD) (((st.wYear-1980) << 9) | (st.wMonth << 5) | st.wDay);
- }
-
- // Scan subdirectory and update the directory structure
- static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
- {
- WIN32_FIND_DATA fd;
-
- HANDLE h = FindFirstFile(path_ + "*", &fd);
- if (h)
- {
- // Some notes on efficiency:
- //
- // 1) It is probably faster to fill a view with data, and
- // then store it in a persistent field, than to modify
- // persistent structures in place. In this example, this
- // is needed anyway, since we also sort all filenames.
- //
- // 2) If possible, avoid constructing rows inside a loop.
- // For that reason, both 'prop [const]' and 'row + row'
- // are relatively inefficient.
-
- // c4_View files = pFiles (dirs_[dirNum_]);
- c4_View files;
- c4_Row dir, file;
-
- files.SetSize(0, 100); // growth granularity
-
- do
- {
- if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- {
- if (fd.cFileName[0] == '.')
- continue;
-
- // dirs_.Add(pParent [dirNum_] + pName [fd.cFileName]);
- pParent (dir) = dirNum_;
- pName (dir) = fd.cFileName;
- dirs_.Add(dir);
- }
- else
- {
- // files.Add(pName [fd.cFileName] + pSize [fd.nFileSizeLow]
- // + pDate [DosDate(fd.ftLastWriteTime)]);
- pName (file) = fd.cFileName;
- pSize (file) = fd.nFileSizeLow;
- pDate (file) = DosDate(fd.ftLastWriteTime);
- files.Add(file);
- }
-
- } while (FindNextFile(h, &fd));
-
- FindClose(h);
-
- pFiles (dirs_[dirNum_]) = files.SortOn(pName);
- }
- }
-
- #else
-
- #include <dos.h>
-
- // Scan subdirectory and update the directory structure
- static void ScanSubDir(c4_View& dirs_, int dirNum_, const CString& path_)
- {
- _find_t fd;
-
- const unsigned mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
- if (_dos_findfirst(path_ + "*.*", mask, &fd) == 0)
- {
- c4_View files;
- c4_Row dir, file;
-
- files.SetSize(0, 100); // growth granularity
-
- do
- {
- if (fd.attrib & _A_SUBDIR)
- {
- if (fd.name[0] == '.')
- continue;
-
- pParent (dir) = dirNum_;
- pName (dir) = fd.name;
- dirs_.Add(dir);
- }
- else
- {
- pName (file) = fd.name;
- pSize (file) = fd.size;
- pDate (file) = fd.wr_date;
- files.Add(file);
- }
-
- } while (_dos_findnext(&fd) == 0);
-
- pFiles (dirs_[dirNum_]) = files.SortOn(pName);
- }
- }
-
- #endif
-
- /////////////////////////////////////////////////////////////////////////////
- // The following variation supports long filenames, as used by Windows 95
-
- #ifdef LFNAMES
-
- #include "lfnames.h"
-
- // Scan subdirectory and update the directory structure
- static void ScanLongDir(c4_View& dirs_, int dirNum_, const CString& path_)
- {
- lfn_find_t fd;
-
- const int mask = _A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR;
- if (lfn_findfirst(path_ + "*.*", mask, &fd) == 0)
- {
- c4_View files;
- c4_Row dir, file;
-
- files.SetSize(0, 100); // growth granularity
-
- do
- {
- CString s = fd.name;
- s.MakeUpper();
-
- // if only upper case, then capitalize the first letter only
- if (s == fd.name && !s.IsEmpty())
- {
- char c = s[0];
- s.MakeLower();
- s.SetAt(0, c);
- }
- else
- s = fd.name;
-
- if (fd.attrib & _A_SUBDIR)
- {
- if (fd.name[0] == '.')
- continue;
-
- pParent (dir) = dirNum_;
- pName (dir) = s;
- dirs_.Add(dir);
- }
- else
- {
- pName (file) = s;
- pSize (file) = fd.size;
- pDate (file) = fd.wr_date;
- files.Add(file);
- }
-
- } while (lfn_findnext(&fd) == 0);
-
- lfn_findclose(&fd);
-
- pFiles (dirs_[dirNum_]) = files.SortOn(pName);
- }
- }
-
- #endif
-
- /////////////////////////////////////////////////////////////////////////////
- // Scan a directory tree and return a corresponding structure for it
-
- c4_View fScanDirectories(const char* path_, cStatusHandler* handler)
- {
- // Start with a view containing the root directory entry
- c4_View dirs;
- dirs.Add(pName [path_]);
-
- #ifdef LFNAMES
- // don't call lfn_api on Win 3.1x, it will crash...
- // NT4 returns 0x0310, this is ok since the lfn rtns don't work on it
- BOOL useLfns = _winver > 0x0311 && lfn_api(path_) != 0;
- #endif
-
- // This loop "automagically" handles the recursive traversal of all
- // subdirectories. The trick is that each scan may add new entries
- // at the end, causing this loop to continue (GetSize() changes!).
-
- for (int i = 0; i < dirs.GetSize(); ++i)
- {
- CString path = fFullPath(dirs, i);
-
- if (handler && !handler->UpdateStatus(path))
- return c4_View ();
-
- #ifdef LFNAMES
- if (useLfns)
- ScanLongDir(dirs, i, path);
- else
- #endif
- ScanSubDir(dirs, i, path);
- }
-
- // The returned object contains the entire directory tree.
- // Everything is automatically destroyed when no longer referenced.
- return dirs;
- }
-
- /////////////////////////////////////////////////////////////////////////////
-